'''
redis
    string  二进制安全， 一个键最大512m
    hash    适合存储对象， 可直接修改属性 ， 场景： 用户信息
    list    增删快
    set
    sorted set  每个元素带一个score

    单机有多个数据库， 集群没有数据库概念
    默认支持16个数据库， 可以修改配置文件调整，select 1 选择数据库
    不同数据库之间数据是隔离的

    不同的应用的数据 应当启动多个redis实例去分开存储

    3. 缓存击穿
        成因：某些key不存在，当请求某些key的并发较高，数据库压力就增大
        解决方案：使用给key 加锁，同时去数据库查询，然后更新缓存，这样减轻数据库压力
    4. 缓存穿透
        成因：有些值本就不存在，数据库也没有，
        
    
    过期策略：
    1. 惰性删除
        访问key的时候，才去判断是否过期
    2. 定期删除
        定期删除过期的key
    3. 内存淘汰
        volatile-lru：从已设置过期时间的key中，移除最近最少使用的key进行淘汰
        volatile-ttl：从已设置过期时间的key中，移除将要过期的key
        volatile-random：从已设置过期时间的key中随机选择key淘汰
        allkeys-lru：从key中选择最近最少使用的进行淘汰
        allkeys-random：从key中随机选择key进行淘汰
        noeviction：当内存达到阈值的时候，新写入操作报错

    发布-订阅模式

    redis 事务， 任意命令执行失败， 其他命令依然会被执行

    geo

    stream

    管道 、
    分区  分区是分割数据到多个Redis实例的处理过程，因此每个实例只保存key的一个子集。

    备份策略：
        AOF 将写操作保存到日志文件中， 恢复时按照日志文件恢复
        RDB 备份数据文件
'''

'''
python 操作redis 事务,   

乐观锁， 利用redis自带的事务机制
'''

import  time
import  random
import  redis
from redis import WatchError
import threading
#
# class CasNormal(object):
#     def __init__(self, host, key):
#         self.r = redis.Redis(host)
#         self.key = key
#         if not self.r.exists(self.key):
#             self.r.set(self.key, 0)
#     def disturb(self):
#         now = self.r.get(self.key)  # 此方法不停地修改这个 key的值， 但没有加事务
#         for i in range(100):
#             print(i)
#             self.r.set(self.key, str(int(now) + random.randint(-10,10)))
#
#     def inc(self):  # 此方法修改10次 key的值， 每次都加了事务
#         for i in range(10):
#             with self.r.pipeline() as pipe:
#                 count = 0
#                 while True:
#                     try:
#                         count +=1
#                         pipe.watch(self.key)
#                         next_count = 30 + int(pipe.get(self.key))
#                         pipe.multi()
#                         if next_count < int(time.time()):
#                             next_count = int(time.time())
#                         pipe.set(self.key, next_count)
#                         pipe.execute()
#                         print('success')
#                         print('次数：%s' % count)
#                         break
#                     except WatchError:
#                         print('error')
#                         continue
#                     finally:
#                         pipe.reset()
#
#
# c = CasNormal('localhost', 'time')
# t1 = threading.Thread(target=c.disturb)
# t1.start()
#
# t2 = threading.Thread(target=c.inc)
# t2.start()

'''
    运行结果可以看出， 这个key被watch后， 一旦有别的线程在更改这一个key， 那么事务就会失败，
    就需要重开事务， 可能会循环很多次才能完成事务。 
    这就是乐观锁， 只能并发量较小的情况下或更新频率较小的情况才比较适用，如果并发较高， 
    就比较消耗时间， 可能造成死锁。
'''

'''
    悲观锁  python代码层面实现
'''
class CasLock():
    lock_conn = redis.Redis('localhost')
    def __init__(self, key):
        self.key = key
        # if not self.lock_conn.exists(self.key):
        self.lock_conn.set(self.key, 0)

    def inc(self):
        lock = threading.Lock()

        if lock.acquire():
            a = int(self.lock_conn.get(self.key)) + 30
            self.lock_conn.set(self.key, a)
            lock.release()

threads = []
for i in range(100):
    c = CasLock('test2')
    t = threading.Thread(target=c.inc)
    threads.append(t)
for i in threads:
    i.start()
for i in threads:
    i.join()

